#include "DataManager.h"

/// Data handle entry constructor
CDataManager::sDataHandleEntry::sDataHandleEntry( )
: 	m_uiNextFreeIndex(0),
	m_uiCounter(1),
	m_uiActive(0),
	m_uiEndOfList(0),
	m_hEntry(NULL)
{ }

/// Data handle entry constructor
/**	@param nextFreeIndex - free index */
CDataManager::sDataHandleEntry::sDataHandleEntry( uint nextFreeIndex )
: 	m_uiNextFreeIndex(nextFreeIndex),
	m_uiCounter(1),
	m_uiActive(0),
	m_uiEndOfList(0),
	m_hEntry(NULL)
{ }

/// Data manager constructor
CDataManager::CDataManager( str repo_dir ) : m_strRepoLocation(repo_dir) {
	Reset();
}

/// Data manager destructor
CDataManager::~CDataManager() {
	// Destroying data in reversed order
	for( uint it_entry = MaxEntries-1; it_entry >= 0; --it_entry ) {
		FREE( m_Entries[it_entry].m_hEntry );
	}
}

/// Reset data manager
void CDataManager::Reset( ) {
	m_iActiveEntryCount	= 0;
	m_uiFirstFreeEntry	= 0;

	for( int i = 0; i < MaxEntries - 1; ++i )
		m_Entries[i] = sDataHandleEntry( i + 1 );

	m_Entries[MaxEntries - 1] = sDataHandleEntry( );
	m_Entries[MaxEntries - 1].m_uiEndOfList = true;
}

/// Add data entry in manager
/**	@param p - data handle
		@param type - data type */
DATAH CDataManager::Add( handle p, uint type ) {
	const int newIndex = m_uiFirstFreeEntry;

	m_uiFirstFreeEntry = m_Entries[newIndex].m_uiNextFreeIndex;
	m_Entries[newIndex].m_uiNextFreeIndex = 0;
	m_Entries[newIndex].m_uiCounter = m_Entries[newIndex].m_uiCounter + 1;
	if( m_Entries[newIndex].m_uiCounter == 0 )
		m_Entries[newIndex].m_uiCounter = 1;
	m_Entries[newIndex].m_uiActive = true;
	m_Entries[newIndex].m_hEntry = p;
	m_Entries[newIndex].m_uiType = type;

	++m_iActiveEntryCount;

	return DATAH( newIndex, m_Entries[newIndex].m_uiCounter, type );
}

/// Update data entry
/**	@param h - data handle to update
		@param p - data to set */
void CDataManager::Update( DATAH h, handle p ) {
	const int index = h.m_uiIndex;
	m_Entries[index].m_hEntry = p;
	m_Entries[index].m_uiType = h.m_uiType;
}

/// Remove data entry from manager
/**	@param h - data handle to remove */
void CDataManager::Remove( const DATAH h ) {
	const uint index = h.m_uiIndex;

	m_Entries[index].m_uiNextFreeIndex = m_uiFirstFreeEntry;
	m_Entries[index].m_uiActive = 0;
	m_uiFirstFreeEntry = index;

	--m_iActiveEntryCount;
}

/// Get data entry from manager
/**	@param h - data handle to get
		@return the data pointer */
handle CDataManager::Get( DATAH h ) const {
	handle p = NULL;
	if( !Get(h, p) )
		return NULL;
	return p;
}

/// Get next data item that is of the same type as the provided one
/**	@param h - provided data handle
		@return the next data handle of the same type */
DATAH CDataManager::GetNextLike( DATAH h ) const {
	int start_index = h.m_uiIndex;
	for( int i = start_index + 1; i < MaxEntries; i++ ) {
		if( m_Entries[i].m_uiType == h.m_uiType && m_Entries[i].m_uiActive )
			return DATAH( i, m_Entries[i].m_uiCounter, m_Entries[i].m_uiType );
	}
	for( int i = 0; i < start_index; i++ ) {
		if( m_Entries[i].m_uiType == h.m_uiType && m_Entries[i].m_uiActive )
			return DATAH( i, m_Entries[i].m_uiCounter, m_Entries[i].m_uiType );
	}
	return h;
}

/// Get previous data item that is of the same type as the provided one
/**	@param h - provided data handle
		@return the previous data handle of the same type */
DATAH CDataManager::GetPreviousLike( DATAH h ) const {
	uint start_index = h.m_uiIndex;
	for( uint i = start_index-1; i >= 0 ; i-- ) {
		if( m_Entries[i].m_uiType == h.m_uiType && m_Entries[i].m_uiActive )
			return DATAH( i, m_Entries[i].m_uiCounter, m_Entries[i].m_uiType );
	}
	for( uint i = MaxEntries - 1; i > start_index; i-- ) {
		if( m_Entries[i].m_uiType == h.m_uiType && m_Entries[i].m_uiActive )
			return DATAH( i, m_Entries[i].m_uiCounter, m_Entries[i].m_uiType );
	}
	return h;
}

/// Get the first data entry of the same type as the provided one
/**	@param type - provided type to match
		@return the first data entry of the provided  type */
DATAH	CDataManager::GetFirstOf( uint type ) const {
	for( uint i = 0; i < MaxEntries; i++ ) {
		if( m_Entries[i].m_uiType == type && m_Entries[i].m_uiActive )
			return DATAH( i, m_Entries[i].m_uiCounter, m_Entries[i].m_uiType );
	}
	return DATAH();
}

/// Get data by filling pointer
/**	@param h - data handle to get
		@param out - [out] get data
		@return true if data is available */
bool CDataManager::Get( const DATAH h, handle &out ) const {
	const int index = h.m_uiIndex;
	if (m_Entries[index].m_uiCounter != h.m_uiCounter ||
	    m_Entries[index].m_uiActive == false)
		return false;

	out = m_Entries[index].m_hEntry;
	return true;
}


/// Get active entry count
/**	@return the count */
int CDataManager::GetCount( ) const {
	return m_iActiveEntryCount;
}

/// Load content file
/**	@param filename - string for the relative/absolute file path
		@param context - the rendering context that will use the data
		@return the data handle of the content */
DATAH CDataManager::FileLoad( str filename, pieARenderer* context ) {
	if( filename.empty() )
		return NO_DATA;

	map<str,DATAH>::iterator it_filedb;
	if( ( it_filedb = m_mapFilenameDatabase.find( filename ) ) != m_mapFilenameDatabase.end( ) ) {
		return it_filedb->second;
	}

	DATAH t_result = NO_DATA;

	str t_extention = filename;
	t_extention.erase( 0, t_extention.find_last_of('.') + 1 );

	//TODO Apply extentions here

	if( t_result )
		m_mapFilenameDatabase[filename] = t_result;

	return t_result;
}

/// Include Data Extention
/**	@param extentionClass - class that represents the extention */
void  CDataManager::IncludeDataExtention( IDataExtention* extentionClass ) {
	#ifdef TZM_BUILD_SAFEMODE
		if ( !extentionClass )
			return;

		for ( list<IDataExtention*>::iterator i = m_listOfDataExtentions.begin(); i != m_listOfDataExtentions.end(); ++i ) {
			if ( extentionClass->GetName( ) == (*i)->GetName( ) && extentionClass != (*i) ) {
				FREE( extentionClass )
				return;
			}
		}
	#endif

	m_listOfDataExtentions.push_back( extentionClass );
}

/// Remove Data Extention
/**	@param fileExtentionOrNameToMatch - self-explanatory */
void  CDataManager::RemoveDataExtention( str fileExtentionOrNameToMatch ) {
	#ifdef TZM_BUILD_SAFEMODE
		if ( fileExtentionOrNameToMatch.empty( ) )
			return;
	#endif

	for ( list<IDataExtention*>::iterator i = m_listOfDataExtentions.begin(); i != m_listOfDataExtentions.end(); ++i ) {
		if ( fileExtentionOrNameToMatch == (*i)->GetName( ) || (*i)->GetFileMask( ).find(fileExtentionOrNameToMatch) != str::npos ) {
			FREE( *i )
			m_listOfDataExtentions.erase( i );
			return;
		}
	}
}
